From 5a0c4de07f75d89e5b84f9dcade7b17c019433d8 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Tue, 28 Mar 2023 23:58:50 +0200 Subject: [PATCH] rendernodeparser: Add support for reusing nodes We extend the syntax for nodes from: { ... } to { ... } { ... } ; where the first is the same as before, the 2nd defines a named node and the last references a previously defined node. Or to give an example: color "node" { bounds: 0 0 10 10; color: red; } transform { bounds: 20 0 10 10; child: "node"; } This will draw the red box twice, once at (0,0) and once at (20,0). The intended use for this is both shortening generated node files as well as allowing to write tests that reuse nodes, in particular when dealing with caches. --- gsk/gskrendernodeparser.c | 63 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c index c74d16660a..07825fd56e 100644 --- a/gsk/gskrendernodeparser.c +++ b/gsk/gskrendernodeparser.c @@ -2136,19 +2136,60 @@ parse_node (GtkCssParser *parser, { "mask", parse_mask_node }, }; GskRenderNode **node_p = out_node; + const GtkCssToken *token; guint i; + token = gtk_css_parser_get_token (parser); + if (gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_STRING)) + if (gtk_css_token_is (token, GTK_CSS_TOKEN_STRING)) + { + GskRenderNode *node; + char *node_name; + + node_name = gtk_css_parser_consume_string (parser); + + if (context->named_nodes) + node = g_hash_table_lookup (context->named_nodes, node_name); + else + node = NULL; + + if (node) + { + *node_p = gsk_render_node_ref (node); + g_free (node_name); + return TRUE; + } + else + { + gtk_css_parser_error_value (parser, "No node named \"%s\"", node_name); + g_free (node_name); + return FALSE; + } + } + for (i = 0; i < G_N_ELEMENTS (node_parsers); i++) { if (gtk_css_parser_try_ident (parser, node_parsers[i].name)) { GskRenderNode *node; + GtkCssLocation node_name_start_location, node_name_end_location; + char *node_name; + + if (gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_STRING)) + { + node_name_start_location = *gtk_css_parser_get_start_location (parser); + node_name_end_location = *gtk_css_parser_get_end_location (parser); + node_name = gtk_css_parser_consume_string (parser); + } + else + node_name = NULL; if (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF)) { gtk_css_parser_error_syntax (parser, "Expected '{' after node name"); return FALSE; } + gtk_css_parser_end_block_prelude (parser); node = node_parsers[i].func (parser, context); if (node) @@ -2156,9 +2197,31 @@ parse_node (GtkCssParser *parser, if (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF)) gtk_css_parser_error_syntax (parser, "Expected '}' at end of node definition"); g_clear_pointer (node_p, gsk_render_node_unref); + + if (node_name) + { + if (context->named_nodes == NULL) + context->named_nodes = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) gsk_render_node_unref); + if (g_hash_table_lookup (context->named_nodes, node_name)) + { + gtk_css_parser_error (parser, + GTK_CSS_PARSER_ERROR_FAILED, + &node_name_start_location, + &node_name_end_location, + "A node named \"%s\" already exists.", node_name); + } + else + { + g_hash_table_insert (context->named_nodes, g_strdup (node_name), gsk_render_node_ref (node)); + } + } + *node_p = node; } + g_free (node_name); + return node != NULL; } } -- 2.30.2